package com.zlc.common.util;

import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.util.StrUtil;
import com.zlc.common.common.exception.BusinessException;
import com.zlc.common.config.RedissonConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class LockerManager {
    @Autowired
    private RedissonConfig config;
    @Autowired
    @Qualifier("mayanRedissonClient")
    private RedissonClient redissonClient;

    public LockerManager() {
    }

    public void lock(Action action, Duration waitTime, Duration leaseTime, String... lockKeys) {
        Func0<Object> func = action == null ? null : () -> {
            action.call();
            return null;
        };
        this.lock(func, waitTime, leaseTime, lockKeys);
    }

    public <R> R lock(Func0<R> func, Duration waitTime, Duration leaseTime, String... lockKeys) {
        if (func != null && lockKeys != null && lockKeys.length > 0) {
            RLock locker;
            if (lockKeys.length == 1) {
                locker = this.redissonClient.getLock(this.config.getLockPrefix() + lockKeys[0]);
            } else {
                locker = this.redissonClient.getMultiLock((RLock[]) Arrays.stream(lockKeys).map((k) -> this.redissonClient.getLock(this.config.getLockPrefix() + k)).toArray((x$0) -> new RLock[x$0]));
            }

            boolean success = false;

            try {
                success = locker.tryLock(waitTime.getSeconds(), leaseTime.getSeconds(), TimeUnit.SECONDS);
            } catch (InterruptedException var17) {
            }

            if (success) {
                Object result;
                try {
                    result = func.call();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    try {
                        locker.unlock();
                    } catch (Exception e) {
                        log.error("[释放锁异常] " + ExceptionUtils.getRootCauseMessage(e));
                    }

                }

                return (R) result;
            }
        }

        throw new BusinessException(StrUtil.join(",", lockKeys));
    }
}